package common;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.PropertyFilter;
import com.alibaba.fastjson.serializer.SerializeWriter;
import com.alibaba.fastjson.serializer.SerializerFeature;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.ReflectionUtils;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Objects;

/**
 * 序列化
 * @description
 * @project	项目管理平台
 * @since	1.0
 */
@Slf4j
public abstract class AbstractSerializer {
    /**
     * 默认字符串序列化工具
     */
    private static final StringRedisSerializer DEFAULT_SERIALIZER = new StringRedisSerializer();

    private StringRedisSerializer stringRedisSerializer = DEFAULT_SERIALIZER;

    public void setStringRedisSerializer(StringRedisSerializer stringRedisSerializer) {
        this.stringRedisSerializer = stringRedisSerializer;
    }

    /**
     * 默认只允许保存最大为1M的内容
     */
    private static final int DEFAULT_MAX_SIZE = 2 << 20;
    private int maxSize = DEFAULT_MAX_SIZE;

    protected byte[] serialize(Object object){
        byte[] datas = serialize1(object);
        checkDataSize(datas);
        return datas;
    }

    protected String serializeToStr(Object object){
        return new String(serialize(object));
    }

    protected byte[] serializeKey(String key){
        return serialize1(key);
    }

    private byte[] serialize1(Object object) {
        if (object instanceof String) {
            synchronized (stringRedisSerializer) {
                return stringRedisSerializer.serialize((String) (object));
            }
        } else {
            PropertyFilter filter = getPropertyFilter();
            return toJSONBytesWithFilter(object, filter, SerializerFeature.SortField);
        }
    }

    @SuppressWarnings("unchecked")
    protected <T> T deserialize(Class<T> type, byte[] bytes) {
        if (Objects.isNull(bytes)){
            return null;
        }
        if (type == String.class) {
            synchronized (stringRedisSerializer) {
                return (T) stringRedisSerializer.deserialize(bytes);
            }
        } else {
            return JSON.parseObject(bytes, type, Feature.SortFeidFastMatch);
        }
    }

    protected <T> List<T> deserializeArray(Class<T> type, byte[] bytes) {
        if (Objects.isNull(bytes)){
            return null;
        }
        try {
            return (List<T>) JSON.parseArray(new String(bytes, "UTF-8"), type);
        } catch (UnsupportedEncodingException e) {
            log.error("DeserializeArray error");
        }
        return null;
    }


    private final byte[] toJSONBytesWithFilter(Object object, PropertyFilter filter, SerializerFeature... features) {
        SerializeWriter out = new SerializeWriter();

        try {
            JSONSerializer serializer = new JSONSerializer(out);
            serializer.getPropertyFilters().add(filter);

            for (SerializerFeature feature : features) {
                serializer.config(feature, true);
            }

            serializer.write(object);

            return out.toBytes("UTF-8");
        } finally {
            out.close();
        }
    }
    private PropertyFilter getPropertyFilter() {
        PropertyFilter filter = new PropertyFilter() {
            @Override
            public boolean apply(Object object, String name, Object value) {
                Field field = ReflectionUtils.findField(object.getClass(), name);

                if (field == null) {
                    return false;
                }
//                CacheJsonField cacheJsonField = field.getAnnotation(CacheJsonField.class);
//                if (cacheJsonField == null) {
//                    return true;
//                } else {
//                    return cacheJsonField.serialize();
//                }
                return false;
            }
        };
        return filter;
    }

    private byte[] checkDataSize(byte[] datas){
        if(datas.length>maxSize){
            log.warn("保存内容长度大于：" + maxSize);
            throw new RuntimeException("保存内容长度大于：" + maxSize);
        }
        return datas;
    }

    public void setMaxSize(int maxSize) {
        this.maxSize = maxSize;
    }
}